1.ExoPlayer简介
ExoPlayer是google开源的应用级媒体播放器项目。
与内置的MediaPlayer相比,ExoPlayer的优点主要有:
- 支持通过Http(DASH)和SmoothStreaming进行动态自适应流,这两种都不受MediaPlayer的支持。它还支持其他格式的数据资源,比如MP4、M4A、FMP4、MKV、MP3、Ogg、WAV、FLV等
- 支持高级的HLS个性,比如正确处理#EXT-X-DISCONTINUITY标签
- 无缝连接,合并和循环播放多媒体的能力
- 和应用一起更新播放器(ExoPlayer),因为ExoPlayer是一个集成到应用APK的库,可以随着应用的更新把ExoPlayer更新到一个更新的版本。
- 在不同的Android版本和设备上很少会有不同的表现。
- 能够自定义和扩展播放器,以适应各种不同的需求
- 各个组件都可以自定义,还可以接入ffmpeg组件
缺点就是,在音频播放时ExoPlayer会比MediaPlayer消耗更多的电量。
集成
在app的module中的build.gradle
文件添加如下依赖(这种方式是添加全部的依赖):implementation 'com.google.android.exoplayer:exoplayer:2.X.X'
如果不需要依赖全部的类库,也可以只选择添加你需要的类库,例如下面的方式是只需要播放DASH内容的app,就是只添加Core、DASH和UI库:
implementation 'com.google.android.exoplayer:exoplayer-core:2.X.X'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.X.X'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.X.X'
下面列出了所有的依赖库:
exoplayer-core
:核心功能库(基础库、必要)exoplayer-dash
:支持DASHexoplayer-hls
:支持HLSexoplayer-smoothstreaming
:支持SmoothStreamingexoplayer-ui
:使用ExoPlayer所需的UI部分和资源
上面讲到了DASH、HLS、SmoothStreaming,具体的介绍可以参考流媒体通信协议
除了上面的几个类库外,ExoPlayer还有很多扩展库提供一些额外的功能,有一些可以直接通过JCenter进行依赖,有一些需要手动去编译,具体的可以通过扩展库目录中的README来查看详细的内容。 可以通过JCenter进行依赖的类库和扩展可以从ExoPlayer的Binatry上查看。
如果依赖完后仍然不行,你需要将build.gradle文件中的android节点打开Java 8的支持:
compileOptions {
targetCompatibility = 1.8
}
ExoPlayer库的核心是ExoPlayer接口,ExoPlayer公开了传统的高级媒体播放器功能,例如缓冲、播放、暂停、seek等。在具体实现方面,该开源库对播放器的媒体类型、存储方式、位置、渲染方式等进行了最少的实现,旨在让开发者自定义各种特性。ExoPlayer的实现不是直接实现加载和呈现媒体,而是将这项工作委托给各种组件。主要有:
- TrackSelector:轨道提取器,从MediaSource中提取各个轨道的二进制数据,交给Renderer渲染,创建播放器时传入。
- Renderer:对多媒体中的各个轨道(音轨、视频轨、字母轨等)数据进行渲染,渲染就是"播放",把二进制文件渲染成声音、画面,创建播放器时传入。
- MediaSource:定义多媒体数据源,这个类的功能就是从Uri中读取多媒体文件的二进制数据。MediaSource在播放开始时通过ExoPlayer.prepare()注入。
- LoadControl:对MediaSource进行控制,比如什么时候开始缓冲、缓冲多少等。
它们之间的关系是:
渲染器(Render) ---刷数据--->提取器(Extraor) ----读取数据---> 加载控制器(LoadControl) ----控制数据加载方式---> 媒体源(MediaSource)
该库提供了这些组件的默认实现,既能满足大部分需求,也可通过自定义来实现特殊的需求。例如可以通过自定义LoadControl来更改播放器的缓冲策略,或自定义Renderer来渲染Android本身不支持的编解码器。
创建播放器
为了满足不同的需求,ExoPlayer提供了一个工厂类ExoPlayerFactory,通过该工厂类来创建一个ExoPlayer实例,大多数情况下直接使用ExoPlayerFactory.newSimpleInstance(context)
方法即可:
public static SimpleExoPlayer newSimpleInstance(Context context) {
return newSimpleInstance(context, new DefaultTrackSelector());
}
它返回的SimpleExoPlayer是一个实现ExoPlayer接口并添加了一些额外的高级播放器功能。
把播放器实例附着到一个View上
ExoPlayer库提供了一个PlayerView(A high level view for Player media playbacks. It displays video, subtitles and album art during playback, and displays playback controls using a PlayerControlView.)类,他封装了PlayerControlView和渲染视频的一个默认的SurfaceView以及字幕等功能。可以通过xml中surface_type来指定视频播放的Surface类型,除了值spherical_view(这是球形视频播放一个特殊的值)时,允许值是surface_view,texture_view和none。如果视图仅用于音频播放,则应使用none以避免必须创建Surface,因为这样做可能耗费资源。
如果视图是用于常规视频播放那么surface_view或texture_view 应该使用。对于视频播放,相比TextureView,SurfaceView有许多好处:
- 显着降低了许多设备的功耗。
- 更准确的帧定时,使视频播放更流畅。
- 播放受DRM保护的内容时支持安全输出。 因此,相比较于TextureView,SurfaceView应尽可能优先考虑。 TextureView只有在SurfaceView不符合您需求的情况下才能使用。一个示例是在Android N之前需要平滑动画或滚动视频表面,如下所述。对于这种情况,最好 TextureView 只在SDK_INT小于24(Android N)时使用, 否则,使用SurfaceView。
SurfaceView在Android N之前,渲染未与视图动画正确同步。在早期版本SurfaceView中,当放入滚动容器或受到动画影响时,这可能会导致不必要的效果 。这些效果包括视图的内容看起来略微落后于它应该显示的位置,并且视图在受到动画时变黑。为了在Android N之前实现流畅的动画或视频滚动,因此必须使用TextureView而不是SurfaceView。
xml声明:
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/mPlayerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
private lateinit var mPlayer: SimpleExoPlayer
private fun initView(context: Context) {
val view = View.inflate(context, R.layout.video_view, this)
mPlayer = ExoPlayerFactory.newSimpleInstance(context)
// 通过PlayerView.setPlayer方法来将ExoPlayer绑定到View上
mPlayerView.player = mPlayer
}
开始播放
ExoPlayer中将每一种媒体资源都封装成MediaSource,如果想要播放一种媒体资源,首先需要为他创建对应的MediaSource对象,然后把这个对象传递给ExoPlayer.prepared方法。ExoPlayer提供了多种MediaSource的实现类,例如播放DASH的DashMediaSource,SmoothStreaming的SsMediaSource,HLS的HlsMediaSource,以及顺序流媒体文件的ProgressiveMediaSource(插一嘴,流式传输分两种方法:实时流式传输方式(Realtime Streaming)和顺序流式传输方式(Progressive Streaming)。)。
fun playSingleMp4Video(url: String) {
val uri = Uri.parse(url)
val mediaSource = buildMediaSource(uri)
// setPlayWhenReady()方法可以设置prepared完成后是否自动播放,如果已经准备好了该方法可实现开始和暂停播放的功能
mPlayer.playWhenReady = true
// setShuffleModeEnabled控制播放列表 setPlaybackParameters控制亮度和音量
// 设置监听
mPlayer.addListener(mPlayerEventListener)
mPlayer.addVideoListener(mPlayerVideoListener)
// 移除监听
// mPlayer.removeListener(mPlayerEventListener)
mPlayer.prepare(mediaSource, false, false)
}
private fun buildMediaSource(uri: Uri): MediaSource {
val dataSourceFactory = DefaultDataSourceFactory(context, Util.getUserAgent(context, "you application name"))
return ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(uri)
}
private var mPlayerEventListener = object : EventListener {
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
when (playbackState) {
Player.STATE_IDLE -> {
// 出事状态、播放器停止或播放失败时的状态
}
Player.STATE_BUFFERING -> {
// 开始加载数据,无法立即从当前位置进行播放
}
Player.STATE_READY -> {
// ready状态,可以直接从当前位置播放
}
Player.STATE_ENDED -> {
// 播放完成
}
}
}
override fun onPlayerError(error: ExoPlaybackException?) {
// error.getSourceException()可以获取更多信息
when(error?.type) {
ExoPlaybackException.TYPE_SOURCE -> {
// 加载资源时出错
}
ExoPlaybackException.TYPE_RENDERER -> {
// 渲染时出错
}
ExoPlaybackException.TYPE_UNEXPECTED -> {
// 意外
}
ExoPlaybackException.TYPE_OUT_OF_MEMORY -> {
// OOM
}
ExoPlaybackException.TYPE_REMOTE -> {
// 远程错误
}
}
}
}
private var mPlayerVideoListener = object : VideoListener {
override fun onVideoSizeChanged(
width: Int,
height: Int,
unappliedRotationDegrees: Int,
pixelWidthHeightRatio: Float
) {
}
override fun onRenderedFirstFrame() {
}
override fun onSurfaceSizeChanged(width: Int, height: Int) {
}
}
资源释放
if (mPlayer != null) {
mPlayer.removeListener(mPlayerEventListener)
mPlayer.removeVideoListener(mPlayerVideoListener)
mPlayer.release()
}
下一篇: 2. ExoPlayer MediaSource简介
- 邮箱 :[email protected]
- Good Luck!